#include <boost/asio/write.hpp>
#include <boost/asio/read.hpp>
#include <boost/asio/ssl.hpp>

#include "mpRandomUtils.h"
#include "mpHttpSslConnection.h"
#include "host_name_verification.h"

#include "mpLog.h"
//引入SSL_set_tlsext_host_name
#include "openssl/tls1.h"

namespace MP{
namespace HTTP{
namespace INTERNAL{

HttpSslConnection::HttpSslConnection(boost::asio::io_context& io_context,
                                     const std::string& host,
                                     const SslSettings& sslSettings)
    :HttpConnection(),
      //_io_context(io_context),
      _ssl_context(boost::asio::ssl::context::sslv23),
      _ssl_socket(io_context,_ssl_context),
      _connected(false)
{
    _id = MP::RandomUtils::getUUID();

    _ssl_context.set_default_verify_paths();
    if (!sslSettings.client_certificate_file.empty())
    {
        _ssl_context.use_certificate_file(sslSettings.client_certificate_file, boost::asio::ssl::context_base::pem);
    }
    if (!sslSettings.client_private_key_file.empty())
    {
        _ssl_context.use_private_key_file(sslSettings.client_private_key_file, boost::asio::ssl::context_base::pem);
    }
    if (!sslSettings.certificate_authority_bundle_file.empty())
    {
        _ssl_context.use_certificate_chain_file(sslSettings.certificate_authority_bundle_file);
    }

    // 如果服务器上存在多个证书（部署有多个域名），那么可通过此方法指定服务端返回的指定域名下的证书，以便进行正确的验证
    // 与openssl该方法类似：openssl s_client -connect 域名:443 -servername 域名
    SSL_set_tlsext_host_name(_ssl_socket.native_handle(), host.c_str());
    // Perform SSL handshake and verify the remote host's certificate.
    // 执行SSL握手并验证远程主机的证书
    //_ssl_socket.set_verify_mode(boost::asio::ssl::verify_none);
    _ssl_socket.set_verify_mode(boost::asio::ssl::verify_peer);
    //    _ssl_socket.set_verify_callback(boost::asio::ssl::rfc2818_verification(host));
    _ssl_socket.set_verify_callback(boost::asio::ssl::host_name_verification(host));
}


//设置或读取最近一次使用HttpConnection的时间
void HttpSslConnection::setLastUseTime(time_t clock) {
    _lastUseTime = clock;
}

time_t HttpSslConnection::getLastUseTime() const {
    return _lastUseTime;
}

void HttpSslConnection::setPoolKey(const std::string& poolKey){
    _poolKey = poolKey;
}

//返回连接在Pool中对应的Key
const std::string& HttpSslConnection::getPoolKey() const {
    return _poolKey;
}

//返回连接ID，Connection的唯一标识
const std::string& HttpSslConnection::getId() const {
    return _id;
}

HttpSocket& HttpSslConnection::getHttpSocket(){
    return _ssl_socket.lowest_layer();
}

//void HttpSslConnection::preConnect(const std::string & host){

//}

void HttpSslConnection::handshakeAsync(func_handshake_callback&& handler) {
    _ssl_socket.async_handshake(boost::asio::ssl::stream_base::client,std::move(handler));
}

void HttpSslConnection::writeSomeAsync(const std::vector<boost::asio::const_buffer>& buffers,
                                       func_write_callback&& handler) {
    boost::asio::async_write(_ssl_socket, buffers,std::move(handler));
}

//void HttpSslConnection::readUntilAsync(std::string & input_buffer,
//                                       const std::string & delim,
//                                       func_read_callback&& handler){

//    boost::asio::async_read_until(_ssl_socket, boost::asio::dynamic_buffer(input_buffer), delim,std::move(handler));
//}

void HttpSslConnection::readSomeAsync(const boost::asio::mutable_buffer& buffers,
                                      func_read_callback&& handler){

    boost::asio::async_read(_ssl_socket, buffers,boost::asio::transfer_at_least(1),std::move(handler));
}

//取消当前操作
void HttpSslConnection::cancle(){
    if(_ssl_socket.lowest_layer().is_open()){
        boost::system::error_code err;

        _ssl_socket.lowest_layer().cancel(err);
        if (err) {
            MPLOG_ERROR << "ssl connection socket cancel error ." << err.message();
            err.clear();
        }
        else{
#if defined(MAPLE_MODE_DEBUGGING)
            MPLOG_INFO << "ssl connection socket is canceled ( " << getId() << " )";
#endif
        }
    }
    else{
#if defined(MAPLE_MODE_DEBUGGING)
        MPLOG_INFO << "ssl connection socket is not open, cancle OP cancle . ( " << getId() << " )";
#endif
    }
}

//设置已连接
void HttpSslConnection::changeToConnect(){
    _connected = true;
}

//断开连接
void HttpSslConnection::disConnected() {
    if(_connected){
        _connected = false;

        if(_ssl_socket.lowest_layer().is_open()){

            boost::system::error_code err;

            //_ssl_socket.shutdown();

            _ssl_socket.lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_type::shutdown_both, err);
            if (err) {
                MPLOG_ERROR << "ssl connection socket shutdown error ." << err.message();
                err.clear();
            }
            else{
    #if defined(MAPLE_MODE_DEBUGGING)
                MPLOG_INFO << "ssl connection socket is shutdown ( " << getId() << " )";
    #endif
            }


            _ssl_socket.lowest_layer().close(err);
            if (err) {
                MPLOG_ERROR << "ssl connection socket close error ." << err.message();
                err.clear();
            }
            else{
    #if defined(MAPLE_MODE_DEBUGGING)
                MPLOG_INFO << "ssl connection socket is closed ( " << getId() << " )";
    #endif
            }
        }
        else{
    #if defined(MAPLE_MODE_DEBUGGING)
            MPLOG_INFO << "ssl connection socket is not open, disConnected OP skip . ( " << getId() << " )";
    #endif
        }
    }
    else{
#if defined(MAPLE_MODE_DEBUGGING)
        MPLOG_INFO << "ssl connection socket is not connected, disConnected OP cancel . ( " << getId() << " )";
#endif
    }
}

//返回连接是否已连接上
bool HttpSslConnection::isConnected(){
    return _connected && _ssl_socket.lowest_layer().is_open();
}



//是否为HTTPS加密连接
bool HttpSslConnection::isSsl() const {
    return true;
}

}   //INTERNAL
}   //HTTP
}   //MP
